Skip to content

docs-audit: state where every emitted anchor came from - #13738

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance
Aug 31, 2026
Merged

docs-audit: state where every emitted anchor came from#13738
os-project-manager merged 1 commit into
mainfrom
claude/issue-12824-anchor-provenance

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes #12824

Implements option C of the maintainer's ruling of 2026-08-31 (verbatim 「同意」), and only C: every row the docs-drift advisory emits now states where its anchor came from — the member and the declaration enclosing it — so a reader can judge the row instead of guessing.

before:  organizationId (symbol)
after:   organizationId (symbol, a field of interface MetaOverlayCacheKey)
after:   userActions    (symbol, a field of const object ObjectSchemaBase)

Those last two are the same syntactic formname: inside an object or interface — and that is the whole finding. One is a field of an internal cache struct that lands 10 pages documenting an unrelated organizationId; the other is the canonical authorable key whose row is the best this tool produces. The card's three disproven discriminators (syntactic form, declaring package, property name against the authorable registry) all fail to separate them. The declaring container separates them, and no row printed it before.

Scope — what this deliberately is not

Proof that the anchor set is unchanged — measured, not asserted

Both arms of affected-docs.mjs (origin/main and this branch) were run against the same tree at the same commit over 100 consecutive main commits touching packages/, comparing the full --json output:

commits replayed 100
rows, base arm 550
rows, this branch 550
anchors emitted 722
anchors carrying provenance 722 (100%)
mismatches 0

Compared per commit: the anchor set as kind + token; the docs list; and the entire JSON document once the two additive fields (anchors[].from, and the clause appended inside each detail[].via string) are removed. All three byte-identical on every commit. Harness and per-commit records are in the dev report on #12824.

The same property is pinned in --self-test rather than left to this one measurement: the provenance key set is asserted to be exactly the anchor set, in both directions — never a superset (a name nothing minted) and never a subset (an anchor with no clause). A future change cannot start deciding with from without going red.

The ruling's own example, end to end

Driven through the real pipeline by committing a one-line widening at each declaration site and running both arms at HEAD^:

edit base row this branch rows
organizationId?: string on MetaOverlayCacheKey organizationId (symbol) organizationId (symbol, a field of interface MetaOverlayCacheKey) 10 → 10
userActions on ObjectSchemaBase userActions (symbol) userActions (symbol, a field of const object ObjectSchemaBase) 5 → 5

content/docs/data-modeling/objects.mdx — the true positive option B drops — is present in both arms, unchanged.

Every anchor kind names its origin

symbol is the card's subject, but the card also records the bridge amplification it feeds (publishItem (sdk) and /:type/:name/publish (route) on a diff touching no state machine), and that is only judgeable when the row names the hop it rode. From a real run on this tree:

sdk    meta.getView                    the route ledger binds it to GET /api/v1/ui/view/:object/:type
route  /view/:object/:type             bridged from symbol enforceEnvironmentOwnership — its registrar handler names it
route  /api/v1/ui/view/:object/:type   a path literal in RestServer
symbol computeExecCtx                  a method of class RestServer

Measured cost

The advisory comment gets longer, which the ruling accepted implicitly (C "does not reduce the row count"). Quantified on the worst run in the 100-commit population: the rendered row block grows 1265 → 2312 bytes (x1.8) at the 15-row display cap. That worst case is command anchors, whose clause was first drafted as the CLI command id ID, read off FILE — a restatement of the token — and is now just read off FILE, which took the worst single row from 2201 to 1549 chars. Median row is 76 chars, p90 222.

⛔ The display-cut problem itself is not addressed here and survives, exactly as the card says of option C.

Verification

  • node scripts/docs-audit/affected-docs.mjs --self-test503 cases pass (487 before; +16 pins for the provenance derivation, the member-form classifier including its degraded answer, and the key-set invariant).
  • node scripts/docs-audit/check-affected-docs.mjs — exit 0 (self-test + --bridge-coverage; discovered population unchanged).
  • node scripts/docs-audit/check-drift-comment.mjs56 cases pass across 5 fixture diffs. This runs the workflow's real comment script against real mapper output, so it is also the check that the added clause does not break the renderer.
  • pnpm lint (repo-wide, eslint . --no-inline-config) — exit 0.
  • Derived gate family (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack), all exit 0: check:nul-bytes, check:docs-audit-scope, check:watch-hint-literal, check:agent-test-spelling, check:entry-guard, check:parse-guard, check:cli-command-ids, check:pnpm-filter-targets, check:bash32-floor, check:pm-governed-merges, check:cross-package-test-inputs, check-self-test-wired, check-ci-filter-parity, check-shard-attestation.
  • check-test-completeness.mjsNOT MEASURED (PREREQUISITE NOT MET, exit 3): it grades a saved turbo run test log and the family names it with no argument. Not a red.
  • Reverse verification, on the committed tree, mutation and restore both proven on disk: inverting memberFormOn's verdict reds 8 pins; withholding the provenance write reds the 4 key-set invariant pins. Restored blob 56b1f118… matches HEAD and git diff HEAD is empty in both cases.

All of the above ran at 8087897a7, the branch head.

No changeset: this edits a CI-internal tooling script and its README and publishes nothing from any package — the skip-changeset case the workflow prescribes.


Generated by Claude Code

Each row of the docs-drift advisory now names the declaration that minted its
anchor, so a reader can judge the row instead of guessing:

  organizationId (symbol, a field of interface MetaOverlayCacheKey)
  userActions    (symbol, a field of const object ObjectSchemaBase)

Those two are the same syntactic form — `name:` inside an object or interface —
and that is the finding this implements: one is the noisiest anchor the tool
mints, the other the most valuable, and no syntactic or per-package test
separates them. The declaring container does, and no row printed it before.

Publication only. No guard, no threshold and no bridge hop reads the new field;
the emitted anchor set is byte-identical before and after, with only the
rendering differing. `--self-test` pins the provenance key set as exactly the
anchor set in both directions, so a later change cannot start deciding with it
without going red.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation labels Aug 31, 2026
@os-project-manager os-project-manager added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 787d757405db4f3ebbc6ca811948af4438a3fe7apackageMentionDocs.

@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 09:15
@os-project-manager
os-project-manager added this pull request to the merge queue Aug 31, 2026
Merged via the queue into main with commit b3d8a75 Aug 31, 2026
35 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12824-anchor-provenance branch August 31, 2026 09:40
os-project-manager pushed a commit that referenced this pull request Aug 31, 2026
… predicate pair

`isCodeShaped` has called `OS_MODE` an identifier since the shape guard was written
(it is pinned as `SCREAMING_SNAKE` in the self-test's shape cases), while
`literalAnchorsFromLines` accepted only three lowercase-initial shapes and so declined
to mint any anchor from it. One predicate in the pair called the token an identifier,
the other silently called it prose, and nothing reported the split.

Measured both ways over the 60 most recent `packages/**` commits, attributing every
added row to the declaration that minted it (the provenance published by #13738):
rows 374 -> 383 (+2.4%), zero rows lost, `overbroadAnchors` unchanged at 8, and only
2 of 60 runs moved. On `b6d3d76b5` the advisory went from 0 to 2 of the 3 docs pages
that commit edited itself.

Also pins the agreement as an invariant, and pins the disagreements that remain as
deliberate: delegating the literal test to `isCodeShaped` was measured at +9.1% and
admits quoted sentence fragments.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

1 participant